home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 031a / adg_7_8.zip / FRAME.C < prev    next >
C/C++ Source or Header  |  1991-02-21  |  28KB  |  748 lines

  1. /****************************************************************************
  2. Module name: Frame.C
  3. Programmer : Jeffrey M. Richter & Elvira Peretsman.
  4. *****************************************************************************/
  5.  
  6. #include "..\nowindws.h"
  7. #undef NOCOLOR
  8. #undef NOCTLMGR
  9. #undef NODEFERWINDOWPOS
  10. #undef NOGDI
  11. #undef NOKERNEL
  12. #undef NOLSTRING
  13. #undef NOMB
  14. #undef NOMDI
  15. #undef NOMENUS
  16. #undef NOMINMAX
  17. #undef NONCMESSAGES
  18. #undef NOSCROLL
  19. #undef NOSHOWWINDOW
  20. #undef NOSYSCOMMANDS
  21. #undef NOSYSMETRICS
  22. #undef NOTEXTMETRIC
  23. #undef NOUSER
  24. #undef NOWH
  25. #undef NOWINMESSAGES
  26. #undef NOWINOFFSETS
  27. #undef NOWINSTYLES
  28. #define OEMRESOURCE
  29. #include <windows.h>
  30.  
  31. #include "mdi.h"
  32.  
  33. static char _szClassName[] = "Frame";
  34.  
  35. // Structure for use with Class Extra Bytes.
  36. typedef struct {
  37.    WORD  wNumSheets;    // Number of Sheet windows created.
  38.    WORD  wNumCharts;    // Number of Chart windows created.
  39.    HMENU hMenu;         // Menu used when no MDI Children are active.
  40.    BOOL  fStatusBarOn;  // Is the status bar showing.
  41.    HWND  hWndMenuHelp;  // Window that last received a WM_MENUSELECT message.
  42.    DWORD dwMenuHelp;    // Menu help code placed here by hWndMenuHelp window.
  43. } CLSEB;
  44.  
  45. void NEAR PASCAL TileVertically (HWND hWndMDIClient);
  46. BOOL FAR PASCAL AboutProc (HWND hDlg, WORD wMsg, WORD wParam, LONG lParam);
  47.  
  48. LONG FAR PASCAL FrameWndProc (HWND hWnd, WORD wMsg, WORD wParam, LONG lParam) {
  49.    BOOL fCallDefProc = FALSE;
  50.    DWORD dwResult = 0;
  51.  
  52.    CLIENTCREATESTRUCT ccs;
  53.    HMENU hMenu;
  54.    RECT rc, rcTemp;
  55.    WORD wTemp = 0;
  56.    FARPROC fpProc;
  57.    char szBuf[100];
  58.    PAINTSTRUCT ps;
  59.    TEXTMETRIC tm;
  60.    HPEN hPen;
  61.    BITMAP Bitmap;
  62.    HWND hWndActiveMDIChild, hWndChild;
  63.    BOOL fMDIChildIsMaximized;
  64.  
  65.    if (IsWindow(_hWndMDIClient))
  66.       dwResult = SendMessage(_hWndMDIClient, WM_MDIGETACTIVE, 0, 0);
  67.  
  68.    // Get the window handle of the active MDI Child.
  69.    // This is NULL if no MDI Children exist.
  70.    hWndActiveMDIChild = (HWND) LOWORD(dwResult);
  71.  
  72.    // Determine if the MDI Child is maximized.
  73.    fMDIChildIsMaximized = HIWORD(dwResult);
  74.    dwResult = 0;
  75.  
  76.    switch (wMsg) {
  77.  
  78.       case WM_CREATE:
  79.          // Initialize default values in the class extra bytes.
  80.          hMenu = LoadMenu(_hInstance, _szClassName);
  81.          SETCLSEB(hWnd, CLSEB, hMenu, hMenu);
  82.          SETCLSEB(hWnd, CLSEB, fStatusBarOn, TRUE);
  83.  
  84.          // Create the MDICLIENT window as a child of the Frame.
  85.          ccs.hWindowMenu = GetSubMenu(GetMenu(hWnd), 1);
  86.          ccs.idFirstChild = IDM_WINDOWCHILD;
  87.  
  88.          _hWndMDIClient = CreateWindow("MDIClient", "",
  89.             WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL |
  90.             WS_VISIBLE | WS_CLIPSIBLINGS, 0, 0, 0, 0, hWnd, NULL, _hInstance,
  91.             (LPSTR) (LPCLIENTCREATESTRUCT) &ccs);
  92.          break;
  93.  
  94.       case WM_CLOSE:
  95.          // Before closing the application, ask the MDI Children if it is OK?
  96.          // wParam is TRUE because the Windows session is NOT being ended.
  97.          fCallDefProc = (BOOL) SendMessage(hWnd, WM_QUERYENDSESSION, TRUE, 0);
  98.          if (fCallDefProc) SendMessage(hWnd, WM_ENDSESSION, TRUE, 0);
  99.          break;
  100.  
  101.       case WM_QUERYENDSESSION:
  102.          // If called by Windows, wParam is zero else wParam is TRUE.
  103.  
  104.          // Assume that it is OK to end the session.
  105.          dwResult = TRUE;
  106.  
  107.          // Get the handle of the first MDI Child.
  108.          hWndChild = GetWindow(_hWndMDIClient, GW_CHILD);
  109.  
  110.          // If no MDI Children exist, it is OK to terminate.
  111.          if (hWndChild == NULL) break;
  112.  
  113.          // Ask each child if it is OK to terminate.
  114.          do {
  115.             // Do not ask caption bars of iconic MDI Children.
  116.             if (GetWindow(hWndChild, GW_OWNER) != NULL) continue;
  117.  
  118.             dwResult = SendMessage(hWndChild, WM_QUERYENDSESSION, wParam, 0);
  119.  
  120.             // If the MDI Child says that it is NOT OK, don't ask the
  121.             // rest of the MDI Children.
  122.             if (dwResult == FALSE) break;
  123.  
  124.          } while ((hWndChild = GetWindow(hWndChild, GW_HWNDNEXT)) != NULL);
  125.  
  126.  
  127.          // If any MDI Child said NO, tell the other children that
  128.          // the session is NOT being terminated.
  129.          if (dwResult == FALSE) {
  130.             wTemp = hWndChild;
  131.             hWndChild = GetWindow(_hWndMDIClient, GW_CHILD);
  132.             do {
  133.                // If this child is the one that said NO, stop.
  134.                if (wTemp == hWndChild) break;
  135.  
  136.                // Do not send to caption bars of iconic MDI Children.
  137.                if (GetWindow(hWndChild, GW_OWNER) != NULL) continue;
  138.  
  139.                // Tell child we are not ending the session (wParam is FALSE).
  140.                SendMessage(hWndChild, WM_ENDSESSION, FALSE, 0);
  141.             } while ((hWndChild = GetWindow(hWndChild, GW_HWNDNEXT)) != NULL);
  142.          }
  143.          // dwResult is TRUE if OK, FALSE if not Ok.
  144.          break;
  145.  
  146.       case WM_ENDSESSION:
  147.          // wParam != FALSE if shutting down.
  148.  
  149.          // Get handle of first MDI Child window.
  150.          hWndChild = GetWindow(_hWndMDIClient, GW_CHILD);
  151.  
  152.          // If no MDI Children exist, we are done.
  153.          if (hWndChild == NULL) break;
  154.  
  155.          // Tell each MDI Child whether or not the session is ending.
  156.          do {
  157.             // Do not send to caption bars of iconic MDI Children.
  158.             if (GetWindow(hWndChild, GW_OWNER) != NULL) continue;
  159.  
  160.             SendMessage(hWndChild, WM_ENDSESSION, wParam, 0);
  161.          } while ((hWndChild = GetWindow(hWndChild, GW_HWNDNEXT)) != NULL);
  162.          break;
  163.  
  164.       case WM_DESTROY:
  165.          PostQuitMessage(0);
  166.          break;
  167.  
  168.       case WM_SYSCOMMAND:
  169.          // Set focus to frame window.  This causes any comboboxes
  170.          // in the ribbon to be closed.
  171.          SetFocus(hWnd); 
  172.          fCallDefProc = TRUE;
  173.          break;
  174.  
  175.       case WM_NCLBUTTONDBLCLK:
  176.          // Code to allow double-clicking the MDI Child's system menu
  177.          // to close the MDI Child window.
  178.          fCallDefProc = TRUE;
  179.  
  180.          // If mouse wasn't clicked in the application's menu, nothing to do.
  181.          if (wParam != HTMENU) break;
  182.  
  183.          // If the active child is not maximized, nothing to do.
  184.          dwResult = SendMessage(_hWndMDIClient, WM_MDIGETACTIVE, 0, 0);
  185.          if (HIWORD(dwResult) != 1) break;
  186.  
  187.          // Get position and dimensions of the MDI Child's system menu in 
  188.          // the Frame's menu bar.
  189.  
  190.          // Get position and dimensions of the Frame window.
  191.          GetWindowRect(hWnd, &rc);
  192.  
  193.          // Get handle to the CLOSE BOX bitmaps.
  194.          wTemp = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_CLOSE));
  195.  
  196.          // Get dimensions of the bitmaps.
  197.          GetObject((HBITMAP) wTemp, sizeof(BITMAP), (LPSTR) (LPBITMAP) &Bitmap);
  198.          DeleteObject((HBITMAP) wTemp);
  199.  
  200.          // Adjust the rectangle.
  201.          rc.top += GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME);
  202.          rc.bottom =  rc.top + Bitmap.bmHeight;
  203.          rc.left += GetSystemMetrics(SM_CXFRAME);
  204.  
  205.          // The close bitmap includes the Application and MDI Child CLOSE 
  206.          // boxes.  So we only want half of the bitmap's width.
  207.          rc.right = rc.left + Bitmap.bmWidth / 2;
  208.  
  209.          // If the mouse cursor is within this rectangle, tell the 
  210.          // MDI Child window to close.
  211.          if (!PtInRect(&rc, MAKEPOINT(lParam))) break;
  212.          SendMessage(LOWORD(dwResult), WM_SYSCOMMAND, SC_CLOSE, lParam);
  213.          fCallDefProc = FALSE;
  214.          break;
  215.  
  216.       case FW_MDICHILDDESTROY:
  217.          // Message is posted by an MDI Child just before it is destroyed.
  218.  
  219.          // If another MDI Child exists, nothing to do.
  220.          if (hWndActiveMDIChild != NULL) break;
  221.  
  222.          // Set the menu bar and accelerator table to the Frame's defaults.
  223.          ChangeMDIMenu(hWnd, _hWndMDIClient,
  224.             (HMENU) GETCLSEB(hWnd, CLSEB, hMenu), IDM_WINDOWTILEVERT);
  225.          _hAccelTable = NULL;
  226.  
  227.          // Force the status bar to be updated.
  228.          InvalidateRect(hWnd, NULL, TRUE);
  229.  
  230.          // Disable the Ribbon.
  231.          EnableWindow(_hDlgRibbon, FALSE);
  232.          break;
  233.  
  234.       case FW_GETSTATBARRECT:
  235.          // lParam = LPRECT.
  236.          // Get the client area of the Frame window.
  237.          GetClientRect(hWnd, (LPRECT) lParam);
  238.  
  239.          // If the status bar is OFF, set the status bar to have no height.
  240.          if (!GETCLSEB(hWnd, CLSEB, fStatusBarOn)) {
  241.             ((LPRECT) lParam)->top = ((LPRECT) lParam)->bottom;
  242.             break;
  243.          }
  244.          
  245.          // Change the dimensions so that the status bar is the height of 
  246.          // one line of text plus a small border.
  247.          wTemp = GetDC(hWnd);
  248.          GetTextMetrics((HDC) wTemp, &tm);
  249.          ReleaseDC(hWnd, (HDC) wTemp);
  250.          ((LPRECT) lParam)->top = ((LPRECT) lParam)->bottom - tm.tmHeight -
  251.             GetSystemMetrics(SM_CYBORDER);
  252.          break;
  253.  
  254.  
  255.       case FW_DRAWSTATUSDIVIDE:
  256.          // lParam = (LPPAINTSTRUCT) &ps.
  257.          // Draw a line separating the status bar from the MDICLIENT window.
  258.          dwResult = GetSystemMetrics(SM_CYBORDER);
  259.          hPen = CreatePen(PS_SOLID, (int) dwResult, RGB(0, 0, 0));
  260.          hPen = SelectObject(((LPPAINTSTRUCT) lParam)->hdc, hPen);
  261.          MoveTo(((LPPAINTSTRUCT) lParam)->hdc, 0,
  262.             ((LPPAINTSTRUCT) lParam)->rcPaint.top);
  263.          LineTo(((LPPAINTSTRUCT) lParam)->hdc, 
  264.             ((LPPAINTSTRUCT) lParam)->rcPaint.right, 
  265.             ((LPPAINTSTRUCT) lParam)->rcPaint.top);
  266.          hPen = SelectObject(((LPPAINTSTRUCT) lParam)->hdc, hPen);
  267.          DeleteObject(hPen);
  268.          break;
  269.  
  270.       case FW_RESIZEMDICLIENT:
  271.          // Sent when the Frame window is resized or when the status bar 
  272.          // and ribbon are toggled.
  273.          GetClientRect(hWnd, &rc);
  274.  
  275.          if (IsWindow(_hDlgRibbon) && IsWindowVisible(_hDlgRibbon))  {
  276.             // Ribbon is displayed, adjust rectangle.
  277.             GetClientRect(_hDlgRibbon, &rcTemp);
  278.             rc.top += rcTemp.bottom;
  279.             rc.bottom -= rcTemp.bottom;
  280.          }
  281.  
  282.          // Get the dimensions of the status bar rectangle and adjust the 
  283.          // dimensions of the MDICLIENT window.
  284.          SendMessage(hWnd, FW_GETSTATBARRECT, 0, (LONG) (LPRECT) &rcTemp);
  285.          rc.bottom -= rcTemp.bottom - rcTemp.top;
  286.          MoveWindow(_hWndMDIClient, 0, rc.top, rc.right, rc.bottom, TRUE);
  287.          break;
  288.  
  289.       case WM_SIZE:
  290.          // Force MDICHILD window to be resized.
  291.          SendMessage(hWnd, FW_RESIZEMDICLIENT, 0, 0);
  292.          break;
  293.  
  294.       case WM_PAINT:
  295.          // Since the only visible portion of the Frame's client area is 
  296.          // the status bar when it is ON, this must mean that the status
  297.          // bar needs to be repainted.
  298.  
  299.          // Set up the device context.
  300.          BeginPaint(hWnd, &ps);
  301.          SendMessage(hWnd, FW_GETSTATBARRECT, 0, (LONG) (LPRECT) &ps.rcPaint);
  302.          SetBkMode(ps.hdc, TRANSPARENT);
  303.  
  304.          // If an MDI Child exists, the status bar must be updated by it.
  305.          if (hWndActiveMDIChild) {
  306.             SendMessage(hWndActiveMDIChild, AC_PAINTSTATBAR, ps.hdc,
  307.                (LONG) (LPPAINTSTRUCT) &ps);
  308.          } else {
  309.             // No MDI Child exists, the Frame can do whatever it wants here.
  310.             ps.rcPaint.top += (int) SendMessage(hWnd, FW_DRAWSTATUSDIVIDE, 0,
  311.                (LONG) (LPPAINTSTRUCT) &ps);
  312.             LoadString(_hInstance, IDS_FRAMESTATUSBAR, szBuf, sizeof(szBuf));
  313.             TextOut(ps.hdc, 0, ps.rcPaint.top, szBuf, lstrlen(szBuf));
  314.          }
  315.          EndPaint(hWnd, &ps);
  316.          break;
  317.  
  318.       case WM_INITMENU:
  319.          // The user has entered the menu system, set any options.
  320.          CheckMenuItem(wParam, IDM_OPTIONSSTATUS, MF_BYCOMMAND |
  321.             (GETCLSEB(hWnd, CLSEB, fStatusBarOn) ? MF_CHECKED : MF_UNCHECKED));
  322.  
  323.          CheckMenuItem(wParam, IDM_OPTIONSRIBBON, MF_BYCOMMAND |
  324.             (IsWindowVisible(_hDlgRibbon) ? MF_CHECKED : MF_UNCHECKED));
  325.          break;
  326.  
  327.       case FW_SETMENUHELP:
  328.          // Called by the Frame and MDI Children whenever a 
  329.          // WM_MENUSELECT message is received.
  330.          // wParam = HWND of sender.
  331.          // lParam = Menu description code.
  332.  
  333.          // Save the handle of the window sending the message.
  334.          SETCLSEB(hWnd, CLSEB, hWndMenuHelp, (HWND) wParam);
  335.  
  336.          // Save the menu help code that the window sent too.
  337.          SETCLSEB(hWnd, CLSEB, dwMenuHelp, lParam);
  338.  
  339.  
  340.          // When the Frame or MDI Child receive a WM_MENUSELECT message
  341.          // specifying that the menu system is closed 
  342.          // (lParam == MAKELONG(-1, 0)), the menu help should disappear and
  343.          // be replaced by the proper information on the status bar.
  344.  
  345.          if (wParam == NULL) {
  346.             SendMessage(hWnd, FW_GETSTATBARRECT, 0, (LONG) (LPRECT) &rc);
  347.             // Force status bar to be updated.
  348.             InvalidateRect(hWnd, &rc, TRUE);
  349.          }
  350.          break;
  351.  
  352.       case FW_GETMENUHELP:
  353.          // Sent by the Frame or MDI Child when they 
  354.          // receive a AW_PAINTMENUHELP message.
  355.          dwResult = GETCLSEB(hWnd, CLSEB, dwMenuHelp);
  356.          break;
  357.  
  358.       case WM_MENUSELECT:
  359.          // The user has highlighted a menu item.
  360.  
  361.          if (lParam == MAKELONG(-1, 0)) {
  362.             // User has stopped using the menu system.
  363.             SendMessage(hWnd, FW_SETMENUHELP, 0, 0);
  364.             break;
  365.          }
  366.  
  367.          // If wTemp == 0, at end of switch, MDI Child handled the message.
  368.          wTemp = 0;
  369.  
  370.          switch (LOWORD(lParam) & (MF_POPUP | MF_SYSMENU)) {
  371.  
  372.             case 0:
  373.                // wParam is a menu item ID NOT on the app's system menu.
  374.  
  375.                if (hWndActiveMDIChild != NULL)  {
  376.                   // An MDI Child exists.
  377.                   if (fMDIChildIsMaximized) {
  378.  
  379.                      // If menu item from the MDI Child's system menu, set 
  380.                      // the MF_SYSMENU bit in the lParam parameter.
  381.                      wTemp = GetSubMenu(GetMenu(hWnd), 0);
  382.                      if ((int) GetMenuState(wTemp, wParam, MF_BYCOMMAND) != -1)
  383.                         lParam |= MF_SYSMENU;
  384.                   }
  385.  
  386.                   // Make active MDI Child think that it received the 
  387.                   // WM_MENUSELECT message.
  388.                   SendMessage(hWndActiveMDIChild, wMsg, wParam, lParam);
  389.                   wTemp = 0;  // MDI Child handled the message.
  390.                   break;
  391.                }
  392.  
  393.                wTemp = IDS_FRAMEMENUID + wParam;
  394.                break;
  395.  
  396.             case MF_POPUP:
  397.                // wParam is handle to popup menu.
  398.  
  399.                if (hWndActiveMDIChild != NULL) {
  400.                   // An MDI Child exists.
  401.                   if (fMDIChildIsMaximized) {
  402.                      // If popup menu is first top-level menu, it is the 
  403.                      // MDI Child's system menu, set the MF_SYSMENU flag.
  404.                      if (wParam == GetSubMenu(GetMenu(hWnd), 0))
  405.                         lParam |= MF_SYSMENU;
  406.                   }
  407.  
  408.                   // Make active MDI Child think that it received the 
  409.                   // WM_MENUSELECT message.
  410.                   SendMessage(hWndActiveMDIChild, wMsg, wParam, lParam);
  411.                   wTemp = 0;  // MDI Child handled the message.
  412.                   break;
  413.                }
  414.  
  415.                // Calculate the index of the top-level menu.
  416.                hMenu = GetMenu(hWnd);
  417.                wTemp = GetMenuItemCount(hMenu);
  418.                while (wTemp--) 
  419.                   if (GetSubMenu(hMenu, wTemp) == (HMENU) wParam) break;
  420.                wTemp += IDS_FRAMEPOPUPID + 1;   // Jump over system menu.
  421.                break;
  422.  
  423.             case MF_SYSMENU:
  424.                // wParam is menu item ID from system menu.
  425.                wTemp = IDS_FRAMEMENUID + ((wParam & 0x0FFF) >> 4);
  426.                break;
  427.  
  428.             case MF_POPUP | MF_SYSMENU:
  429.                // wParam is handle to app's sys menu.
  430.                wTemp = IDS_FRAMEPOPUPID;
  431.                break;
  432.          }
  433.  
  434.          // If message handled by MDI Child, nothing more to do.
  435.          if (wTemp == 0) break;
  436.  
  437.          // Tell the Frame that the Frame window should display the 
  438.          // help text and the identifier for the help text.
  439.          SendMessage(hWnd, FW_SETMENUHELP, hWnd, wTemp);
  440.          break;
  441.  
  442.       case WM_ENTERIDLE:
  443.          if (wParam != MSGF_MENU) break;
  444.  
  445.          // User has stopped scrolling through menu items.
  446.  
  447.          // If Menu help already displayed, nothing more to do.
  448.          // This is signaled by hWndMenu help being -1.
  449.          if (GETCLSEB(hWnd, CLSEB, hWndMenuHelp) == -1)
  450.             break;
  451.  
  452.          // Display new menu help, invalidate the status bar.
  453.          SendMessage(hWnd, FW_GETSTATBARRECT, 0, (LONG) (LPRECT) &rc);
  454.          InvalidateRect(hWnd, &rc, TRUE);
  455.  
  456.          // BeginPaint is OK because an invalid rectangle must exist because
  457.          // of the call to InvalidateRect above.  This causes the background
  458.          // for the Frame's client area to be drawn correctly.
  459.          BeginPaint(hWnd, &ps);
  460.  
  461.          // Set up the device context.
  462.          SetBkMode(ps.hdc, TRANSPARENT);
  463.  
  464.          // Send message to window that last received a WM_MENUSELECT
  465.          // message to tell it to paint the status bar with the 
  466.          // appropriate menu help text.
  467.          SendMessage((HWND) GETCLSEB(hWnd, CLSEB, hWndMenuHelp),
  468.             AW_PAINTMENUHELP, 0, (LONG) (LPPAINTSTRUCT) &ps);
  469.  
  470.          EndPaint(hWnd, &ps);
  471.  
  472.          // Set flag notifying this message that the most recently selected
  473.          // menu item has had its help text painted.  This stops unsightly
  474.          // screen flicker.
  475.          SETCLSEB(hWnd, CLSEB, hWndMenuHelp, (HWND) -1);
  476.          break;
  477.  
  478.       case AW_PAINTMENUHELP:
  479.          // Message sent from Frame window to notify Frame that it should
  480.          // paint the status bar text for the last highlighted menu item.
  481.          // lParam = LPPAINTSTRUCT of Frame's status bar.
  482.  
  483.          // Ask the Frame window what the last selected menu ID was.
  484.          // This value was sent to the frame by this window during the 
  485.          // processing for the WM_MENUSELECT message.
  486.          dwResult = SendMessage(hWnd, FW_GETMENUHELP, 0, 0);
  487.  
  488.          // Draw the horizontal dividing line separating the Status bar
  489.          // from the MDICLIENT window.
  490.          ((LPPAINTSTRUCT) lParam)->rcPaint.top += (int) 
  491.             SendMessage(hWnd, FW_DRAWSTATUSDIVIDE, 0,
  492.             (LONG) (LPPAINTSTRUCT) lParam);
  493.  
  494.          // Construct the string that is to be displayed.
  495.          LoadString(_hInstance, LOWORD(dwResult), szBuf, sizeof(szBuf));
  496.          
  497.          // Paint the menu help text in the status bar.
  498.          TextOut(((LPPAINTSTRUCT) lParam)->hdc,
  499.             0, ((LPPAINTSTRUCT) lParam)->rcPaint.top, szBuf, lstrlen(szBuf));
  500.          break;
  501.  
  502.  
  503.       case WM_COMMAND:
  504.          // If a child is being activated via the "Window" menu, let
  505.          // the DefFrameProc handle it.
  506.          if (wParam >= IDM_WINDOWCHILD) {
  507.             fCallDefProc = TRUE;
  508.             break;
  509.          }
  510.  
  511.          switch (wParam) {
  512.  
  513.             case IDM_FILEOPENSHEET:
  514.                // Get the # of sheets already created and increment by 1.
  515.                wTemp = GETCLSEB(hWnd, CLSEB, wNumSheets) + 1;
  516.                SETCLSEB(hWnd, CLSEB, wNumSheets, wTemp);
  517.  
  518.                // The sheet's caption should display the sheet number.
  519.                wsprintf(szBuf, "Sheet%d", wTemp);
  520.  
  521.                // Create the MDI Child window.
  522.                CreateMDIChild("Sheet", szBuf, 0,
  523.                   CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  524.                   _hWndMDIClient, _hInstance, 0);
  525.  
  526.                // Make sure the ribbon is enabled when any children exist.
  527.                EnableWindow(_hDlgRibbon, TRUE);
  528.                break;
  529.  
  530.             case IDM_FILEOPENCHART:
  531.                // Get the # of charts already created and increment by 1.
  532.                wTemp = GETCLSEB(hWnd, CLSEB, wNumCharts) + 1;
  533.                SETCLSEB(hWnd, CLSEB, wNumCharts, wTemp);
  534.  
  535.                // The chart's caption should display the chart number.
  536.                wsprintf(szBuf, "Chart%d", wTemp);
  537.  
  538.                // Create the MDI Child window.
  539.                CreateMDIChild("Chart", szBuf, 0,
  540.                   CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  541.                   _hWndMDIClient, _hInstance, 0);
  542.  
  543.                // Make sure the ribbon is enabled when any children exist.
  544.                EnableWindow(_hDlgRibbon, TRUE);
  545.                break;
  546.  
  547.             case IDM_OPTIONSSTATUS:
  548.                // Toggle the status of the status bar, resize the MDICLIENT.
  549.                wTemp = !GETCLSEB(hWnd, CLSEB, fStatusBarOn);
  550.                SETCLSEB(hWnd, CLSEB, fStatusBarOn, wTemp);
  551.                SendMessage(hWnd, FW_RESIZEMDICLIENT, 0, 0);
  552.                break;
  553.  
  554.             case IDM_OPTIONSRIBBON:
  555.                // Toggle the status of the ribbon, resize the MDICLIENT.
  556.                ShowWindow(_hDlgRibbon,
  557.                   IsWindowVisible(_hDlgRibbon) ? SW_HIDE : SW_SHOW);
  558.                SendMessage(hWnd, FW_RESIZEMDICLIENT, 0, 0);
  559.                break;
  560.  
  561.             case IDM_EXIT:
  562.                SendMessage(hWnd, WM_CLOSE, 0, 0L);
  563.                break;
  564.  
  565.             case IDM_HELPINDEX:
  566.             case IDM_HELPKEYBOARD:
  567.             case IDM_HELPCOMMANDS:
  568.             case IDM_HELPPROCEDURES:
  569.             case IDM_HELPUSINGHELP:
  570.                MessageBox(hWnd, "Option not implemented.", _szAppName, MB_OK);
  571.                break;
  572.  
  573.             case IDM_ABOUT:
  574.                fpProc = MakeProcInstance(AboutProc, _hInstance);
  575.                DialogBox(_hInstance, "About", hWnd, fpProc);
  576.                FreeProcInstance(fpProc);
  577.                break;
  578.  
  579.             case IDM_WINDOWTILEVERT:
  580.                // Call our own function to perform vertical tiling.
  581.                TileVertically(_hWndMDIClient);
  582.                break;
  583.  
  584.             case IDM_WINDOWTILEHORIZ:
  585.                // Let the MDICLIENT window do the repositioning.
  586.                SendMessage(_hWndMDIClient, WM_MDITILE, 0, 0);
  587.                break;
  588.  
  589.             case IDM_WINDOWCASCADE:
  590.                // Let the MDICLIENT window do the repositioning.
  591.                SendMessage(_hWndMDIClient, WM_MDICASCADE, 0, 0);
  592.                break;
  593.  
  594.             case IDM_WINDOWARRANGEICONS:
  595.                // Let the MDICLIENT window do the repositioning.
  596.                SendMessage(_hWndMDIClient, WM_MDIICONARRANGE, 0, 0);
  597.                break;
  598.  
  599.             default:
  600.                // Menu options not processed by the Frame window must
  601.                // be passed to the MDI Children for processing.
  602.                SendMessage(hWndActiveMDIChild, wMsg, wParam, lParam);
  603.                break;
  604.          }
  605.          break;
  606.  
  607.       default:
  608.          fCallDefProc = TRUE;
  609.          break;
  610.    }
  611.  
  612.    if (fCallDefProc)
  613.       dwResult = DefFrameProc(hWnd, _hWndMDIClient, wMsg, wParam, lParam);
  614.    return(dwResult);
  615. }
  616.  
  617.  
  618. BOOL FAR PASCAL RegisterFrameWndClass (void) {
  619.    WNDCLASS wc;
  620.  
  621.    wc.style          = CS_HREDRAW | CS_VREDRAW;
  622.    wc.lpfnWndProc    = FrameWndProc;
  623.  
  624.    // Number of class extra bytes used by structure.
  625.    wc.cbClsExtra     = sizeof(CLSEB);
  626.  
  627.    wc.cbWndExtra     = 0;
  628.    wc.hInstance      = _hInstance;
  629.    wc.hIcon          = LoadIcon(_hInstance, _szClassName);
  630.    wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
  631.    wc.hbrBackground  = COLOR_WINDOW + 1;
  632.    wc.lpszMenuName   = _szClassName;
  633.    wc.lpszClassName  = _szClassName;
  634.    return(RegisterClass(&wc));
  635. }
  636.  
  637.  
  638. void NEAR PASCAL TileVertically (HWND hWndMDIClient) {
  639.    int nNumWndsOnRow, nOpenMDIChildren = 0, nTopOfBottomIconRow = 0;
  640.    int nCrntCol, nColWidth, nCrntRow, nNumRows, nRowHeight, nMinWndHeight;
  641.    HWND hWndChild;
  642.    HANDLE hWinPosInfo;
  643.    RECT rc;
  644.    POINT Point;
  645.    DWORD dwChildInfo;
  646.  
  647.    // Assume that scrollbars will be off after windows are tiled.
  648.    // By forcing them off now, GetClientRect will return the correct size.
  649.    ShowScrollBar(hWndMDIClient, SB_BOTH, 0);
  650.  
  651.    // The WM_MDICASCADE and WM_MDITILE messages cause the icons to be 
  652.    // arranged.  So we will too.  In fact, this is necessary to locate 
  653.    // the top of the bottom icon row in the next step of this function.
  654.    SendMessage(hWndMDIClient, WM_MDIICONARRANGE, 0, 0);
  655.  
  656.    // Get handle to first MDI Child window.
  657.    hWndChild = GetWindow(hWndMDIClient, GW_CHILD);
  658.    do {
  659.       if (IsIconic(hWndChild) && GetWindow(hWndChild, GW_OWNER) == NULL) {
  660.          // Window is iconic and window is NOT an icon's caption.
  661.  
  662.          // Get client area of the icon window.
  663.          GetWindowRect(hWndChild, &rc);
  664.  
  665.          // rc.top is in screen coordinates.
  666.          nTopOfBottomIconRow = max(nTopOfBottomIconRow, rc.top);
  667.       }
  668.  
  669.       if (!IsIconic(hWndChild) && GetWindow(hWndChild, GW_OWNER) == NULL)
  670.          ++nOpenMDIChildren;
  671.  
  672.    } while ((hWndChild = GetWindow(hWndChild, GW_HWNDNEXT)) != NULL);
  673.  
  674.  
  675.    // All MDI Children are icons, no tiling is necessary.
  676.    if (nOpenMDIChildren == 0) return;
  677.  
  678.  
  679.    // Find height of usable client area for tiling.
  680.    GetClientRect(hWndMDIClient, &rc);
  681.  
  682.    if (nTopOfBottomIconRow) {
  683.       // At least one MDI Child is iconic.
  684.  
  685.       // Convert coordinates from screen to client.
  686.       Point.x = 0; Point.y = nTopOfBottomIconRow;
  687.       ScreenToClient(hWndMDIClient, &Point);
  688.       // Point.y is top of bottom icon row in client coordinates.
  689.       rc.bottom = Point.y;
  690.    }
  691.  
  692.  
  693.    // Restore the active MDI child if it's maximized
  694.    dwChildInfo = SendMessage(hWndMDIClient, WM_MDIGETACTIVE, 0, 0);
  695.    if (HIWORD(dwChildInfo) == 1)
  696.       ShowWindow(LOWORD(dwChildInfo), SW_RESTORE);
  697.  
  698.    // Calculate the minimum desired height of each MDI Child.
  699.    nMinWndHeight = max(1, rc.bottom / (5 * GetSystemMetrics(SM_CYCAPTION)));
  700.  
  701.    // Calculate the number of rows that will be tiled.
  702.    nNumRows = min(nOpenMDIChildren, nMinWndHeight);
  703.  
  704.    // Calculate the height of each row.
  705.    nRowHeight = rc.bottom / nNumRows;
  706.  
  707.    // Get the handle to the first MDI Child window.
  708.    hWndChild = GetWindow(hWndMDIClient, GW_CHILD);
  709.  
  710.    // Prime the storage of positioning information.
  711.    hWinPosInfo = BeginDeferWindowPos(nOpenMDIChildren);
  712.  
  713.    // Execute the loop for each row.
  714.    for (nCrntRow = 0; nCrntRow < nNumRows; nCrntRow++) {
  715.  
  716.       // Calculate the number of MDI Children that will appear on this row.
  717.       nNumWndsOnRow = nOpenMDIChildren / nNumRows +
  718.          ((nOpenMDIChildren % nNumRows > (nNumRows - (nCrntRow + 1))) ? 1 : 0);
  719.  
  720.       // Calculate the width of each of these children.
  721.       nColWidth = rc.right / nNumWndsOnRow;
  722.  
  723.       // Fill each column with an MDI Child window.
  724.       for (nCrntCol = 0; nCrntCol < nNumWndsOnRow; ) {
  725.  
  726.          if (!IsIconic(hWndChild) && GetWindow(hWndChild, GW_OWNER) == NULL) {
  727.             // Child is NOT iconic and not an icon's caption bar.
  728.  
  729.             // Tell windows what the new position and dimensions of this 
  730.             // MDI Child should be.
  731.             hWinPosInfo = DeferWindowPos(hWinPosInfo, hWndChild, NULL,
  732.                nCrntCol * nColWidth, nCrntRow * nRowHeight, nColWidth,
  733.                nRowHeight, SWP_NOACTIVATE | SWP_NOZORDER);
  734.  
  735.             // Go to the next column.
  736.             nCrntCol++;
  737.          }
  738.  
  739.          // Get handle to the next MDI Child window.
  740.          hWndChild = GetWindow(hWndChild, GW_HWNDNEXT);
  741.       }
  742.    }
  743.  
  744.    // All of the positioning has been set.  Now, tell Windows to update
  745.    // all of the windows at once.
  746.    EndDeferWindowPos(hWinPosInfo);
  747. }
  748.